﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;

using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization;
using System.Xml.Serialization;
using FriendlyFire.UserControls;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Net;
using System.Threading;

namespace FriendlyFire
{
    public partial class FriendlyFire : Form
    {
        UserControls.Video video;
        UserControls.Transfers trans;
        UserControls.Search srch;
        UserControls.Indexer id;
        UserControls.PlayerControls plControls;
        UserControls.Home home;

        FFListenerController ctrlListener;

        Friends fForm; // null unless open.

        public Helper ffHelper = new Helper();

        System.Threading.Thread[] streamthreads = { null, null, null, null, null, null, null, null, null, null };
        public System.Drawing.Image[] previewimages = { new System.Drawing.Bitmap("bp.bmp"),  new System.Drawing.Bitmap("bp.bmp"), new System.Drawing.Bitmap("bp.bmp"), new System.Drawing.Bitmap("bp.bmp"), new System.Drawing.Bitmap("bp.bmp") };
        public int currentimage = 0; //int to tell the system which preview to overwrite next
        List<System.Threading.Thread> snapThreads = new List<System.Threading.Thread>();
        public int imagesrecieved = 0;

        // delegates
        private delegate void SetShoutMsgVisibleDelegate(bool vis);
        private delegate void LoadShoutBoxDelegate();
        private delegate void AddnUpdateFriendsListDelegate(FriendsInAList fList);


        /// <summary>
        /// Constructor, sets up the netcode and encrytpion
        /// </summary>
        public FriendlyFire()
        {
            InitializeComponent();
            ctrlListener = new FFListenerController();
            Thread threadedListenServer = new Thread(new ThreadStart(GoStartListenServer));
            threadedListenServer.Start();

            RSACryptoServiceProvider pubkey = new RSACryptoServiceProvider();
            //pubkey.FromXmlString(ffHelper.DBHelper.GetConfig("publickey"));
            RSACryptoServiceProvider privkey = new RSACryptoServiceProvider();
            //privkey.FromXmlString(ffHelper.DBHelper.GetConfig("privatekey"));

            this.Show();
        }

        private void GoStartListenServer()
        {
            ctrlListener.Start();
        }


        //private void AddRecordInTrans(FileReceivedData fileData)
        //{
        //    if (trans.InvokeRequired)
        //    {
        //        trans.Invoke(new RecordInTransDel(AddRecordInTrans), fileData);
        //    }
        //    else
        //    {
        //        if (fileData.Sending)
        //            trans.AddUpload(fileData);
        //        else
        //            trans.AddDownload(fileData);
        //    }
        //}

        //void server_OnClientDisConnected(NetworkClient client)
        //{
        //    client.ClientSocket.Shutdown(SocketShutdown.Both);
        //}

        //void server_FileProgressUpdate(FileReceivedData fileData)
        //{
        //    FileProgressUpdater(fileData);
        //}

        //private delegate void FileProgressUpdateDel(FileReceivedData fileData);
        //private void FileProgressUpdater(FileReceivedData fileData)
        //{
        //    if (trans.InvokeRequired)
        //    {
        //        trans.Invoke(new FileProgressUpdateDel(FileProgressUpdater), fileData);
        //    }
        //    else
        //    {
        //        if (fileData.Sending)
        //            trans.UpdateUpload(fileData);
        //        else
        //            trans.UpdateDownload(fileData);
        //    }
        //}


        //void tClient_FileProgressUpdate(FileReceivedData fileData)
        //{
        //    FileProgressUpdater(fileData);
        //}

        //void tClient_OnFileTransferStart(FileReceivedData fileData)
        //{
        //    AddRecordInTrans(fileData);
        //}

        //void server_OnFileReceived(FileReceivedData fileData)
        //{
            
        //}

        #region Network Command Codes
        /// <summary>
        /// Event raised when data is recieved from a client, decodes the command and executes it
        /// </summary>
        /// <param name="data">Object containing the data recieved</param>
        public void server_dataReceivedEvent(string data)
        {
            #region Code 0
            /*
            if (data.DataType == 0)
            {
                BinaryFormatter bf = new BinaryFormatter();
                DataTable newDT = null;

                newDT = (DataTable)bf.Deserialize(new MemoryStream(data.Data));
                //dataGridView2.DataSource = newDT;
                //trans.SetDataGrid(newDT);
            }*/
            #endregion
            #region Code 231 is for stream request, unique index attached
            /*
            if (data.DataType == 231) //231 is code for stream request, unique index attached
            {                
                string index = Encoding.ASCII.GetString(data.Data);
                string link = ""; 
                for (int i = 0; i < 20; i++) //attempt to read from the db 20 times in case something else is using it, break out and send it when successful
                {
                    try
                    {
                        SubSonic.Generated.File fRow = SubSonic.Generated.File.FetchByID(index);
                        link = fRow.Path + "\\" + fRow.Name;
                        break;
                    }
                    catch
                    {

                    }
                }
                Socket returnUser = data.ClientInfo.ClientSocket;
                RSACryptoServiceProvider rs = new RSACryptoServiceProvider();
                for (int i = 0; i < 10; i++)
                {
                    if (streamthreads[i] == null)
                    {
                        foreach(Client c in Clients )
                        {
                            rs.ImportParameters(data.ClientInfo.PublicKey);
                            if (c.BsPublicKey.Equals(rs.ToXmlString(false)))
                            {
                                vlc_network_thread streamOut = new vlc_network_thread(link, c, i + 1, client);
                                streamthreads[i] = new System.Threading.Thread(new System.Threading.ThreadStart(streamOut.stream));
                                streamthreads[i].Start();
                            }
                        }
                        break;
                    }
                    else if (i == 9)
                    {
                        client.SendData(Encoding.ASCII.GetBytes("No free streams"), 241);
                    }
                }
            }
             */
            #endregion
            #region Code 233 is code for incoming stream on port, port attached
            /*
            if (data.DataType == 233)
            {
                video.killStream();
                video.streamFlag = true;
                string link = "udp://@0.0.0.0:" + Encoding.ASCII.GetString(data.Data);
                RSACryptoServiceProvider rs = new RSACryptoServiceProvider();
                foreach (Client c in Clients)
                {
                    rs.ImportParameters(data.ClientInfo.PublicKey);
                    if (c.BsPublicKey.Equals(rs.ToXmlString(false)))
                    {
                        SetVideoParent(link, c);
                    }
                }
            }
                 */
            #endregion
            #region Code 235 is for request video snapshots, unique database id attached
            /*
            if (data.DataType == 235) //235 is code for request video snapshots, unique database id attached
            {
                string index = Encoding.ASCII.GetString(data.Data); //get the database index of the requested file
                string link = "";
                for (int i = 0; i < 20; i++) //attempt to read from the db 20 times in case something else is using it, break out and send it when successful
                {
                    try
                    {
                        SubSonic.Generated.File fRow = SubSonic.Generated.File.FetchByID(index);
                        link = fRow.Path + "\\" + fRow.Name;
                        break;
                    }
                    catch
                    {

                    }
                }
                Socket returnUser = data.ClientInfo.ClientSocket;
                bool started = false;
                
                for (int i = 0; i < snapThreads.Count(); i++)
                {
                    if (snapThreads[i] == null)
                    {
                        foreach (Client c in Clients)
                        {
                            vlc_network_thread snap = new vlc_network_thread(link, c, i, client);
                            snapThreads[i] = (new System.Threading.Thread(new System.Threading.ThreadStart(snap.snapImages)));
                            snapThreads[i].Start();
                            started = true;
                        }
                    }
                }
                if (!started)
                {
                    foreach (Client c in Clients)
                    {
                        vlc_network_thread snap = new vlc_network_thread(link, c, snapThreads.Count(), client);
                        snapThreads.Add(new System.Threading.Thread(new System.Threading.ThreadStart(snap.snapImages)));
                        snapThreads[snapThreads.Count - 1].Start();
                    }
                }
            }
             */
            #endregion
            #region Code 237 is for killing snap threads once they have completed
            /*
            if (data.DataType == 237)
            {
                snapThreads[System.Convert.ToInt32(Encoding.ASCII.GetString(data.Data))].Abort();
                snapThreads[System.Convert.ToInt32(Encoding.ASCII.GetString(data.Data))] = null;
            }
            #endregion
            #region Code 239 is code for incoming snapshot
            if (data.DataType == 239) 
            {                  
                //check to not step past 5 images
                if (currentimage > 4)
                {
                    currentimage = 0;
                }
                BinaryFormatter bf = new BinaryFormatter();
                MemoryStream ms = new MemoryStream(data.Data);
                previewimages[currentimage] = (System.Drawing.Bitmap)bf.Deserialize(ms);
                currentimage++;
                imagesrecieved++;
            }
             */
            #endregion
            #region Code 241 is code for no free streams
            /*
            if (data.DataType == 241)
            {
                MessageBox.Show("The machine hosting this file has already reached the maximum number of streams.");
            }
             */
            #endregion
            #region Code 301 is code for stop stream on port
            /*
            if (data.DataType == 301)
            {
                int index = System.Convert.ToInt32(Encoding.ASCII.GetString(data.Data)); //find which pos in the array contains the thread we need to turn off
                index -= 9051;
                streamthreads[index].Abort();
                streamthreads[index] = null;
            }
             */
            #endregion
            #region Code 303 is code for pause stream on port
            /*
            if (data.DataType == 303)
            {
                int index = System.Convert.ToInt32(Encoding.ASCII.GetString(data.Data).ToString()) - 9051; //find which pos in the array contains the thread we need to turn off
            }
             */
            #endregion
            #region Code 305 is code for restart stream on port
            /*
            if (data.DataType == 305)
            {
                int index = System.Convert.ToInt32(Encoding.ASCII.GetString(data.Data).ToString()) - 9051; //find which pos in the array contains the thread we need to turn off
            }
             */
            #endregion
            #region Code 307 is code  for seek on port (string "float:port")
            /*
            if (data.DataType == 307)
            {
                int index = System.Convert.ToInt32(Encoding.ASCII.GetString(data.Data).ToString().Split(':')[1]) - 9051; //find which pos in the array contains the thread we need to turn off
                float seek = (float)System.Convert.ToDouble(Encoding.ASCII.GetString(data.Data).ToString().Split(':')[0]);
            }
             */
            #endregion
            #region Code 600
            /*
            if (data.DataType == 600)   // another client is asking to be friends
            {
                BinaryFormatter bf = new BinaryFormatter();
                AddFriendInfo finfo = (AddFriendInfo)bf.Deserialize(new MemoryStream(data.Data));

                //Server.SendData(ASCIIEncoding.UTF8.GetBytes(test), 601, data.ClientInfo.ClientSocket);
                DialogResult dr = MessageBox.Show("Do you wish to accept a friend request from: " + data.ClientInfo.ClientName + "?", "Friend Request", MessageBoxButtons.YesNo);

                if (dr == DialogResult.Yes)
                {
                    // accept the request
                    // send back our public key
                    AddFriendInfo myFFInfo = new AddFriendInfo();
                    myFFInfo.ExternalPort = ffHelper.GetValueByKey("myport");
                    myFFInfo.NickHandle = ffHelper.GetValueByKey("displayname");
                    myFFInfo.DynamicHost = ffHelper.GetValueByKey("dynaminchost");
                    myFFInfo.PublicKey = ffHelper.GetValueByKey("personalkey");
                    myFFInfo.IsLAN = finfo.IsLAN;

                    // serialize the info and send to requestor
                    MemoryStream ms = new MemoryStream();
                    bf.Serialize(ms, myFFInfo);
                    Server.SendData(ms.GetBuffer(), 601, data.ClientInfo.ClientSocket);  //send thanks and my public key

                    SubSonic.Generated.FriendX fFriend = new SubSonic.Generated.FriendX();
                    fFriend.MarkNew();

                    fFriend.Port = ((finfo.IsLAN) ? 9050 : Convert.ToInt32(finfo.ExternalPort));
                    fFriend.Hostname = ((finfo.IsLAN) ? data.ClientInfo.ClientSocket.RemoteEndPoint.ToString().Split(':')[0] : finfo.DynamicHost);
                    fFriend.Handle = finfo.NickHandle;
                    fFriend.OnLAN = finfo.IsLAN;
                    fFriend.RsaparamXMLString = finfo.PublicKey;
                    fFriend.Save();
                    fFriend = null;

                }
                else if (dr == DialogResult.No)
                {
                    // We don't want any!
                    AddFriendInfo myFFInfo = new AddFriendInfo();
                    myFFInfo.PublicKey = "0";
                    MemoryStream ms = new MemoryStream();
                    bf.Serialize(ms, myFFInfo);
                    Server.SendData(ms.GetBuffer(), 601, data.ClientInfo.ClientSocket);
                }
                else
                {
                    // This result is not possible. A universal constant has been savagely violated.
                }
            }
             */
            #endregion
            #region Code 601
            /*
            if (data.DataType == 601)   // friend request acceptance/denial
            {
                BinaryFormatter bf = new BinaryFormatter();
                AddFriendInfo finfo = (AddFriendInfo)bf.Deserialize(new MemoryStream(data.Data));
                if (finfo.PublicKey.Equals("0"))
                {
                    #region Denial
                    // the remote host explicitly denied the friend request
                    string tempHostName = data.ClientInfo.ClientSocket.RemoteEndPoint.ToString();
                    for (int i = 0; i < clientsOnHold.Count; i++)
                    {
                        if (tempHostName.Equals(clientsOnHold[i].ToString()))
                        {
                            // we gots a loser.
                            clientsOnHold.Remove(clientsOnHold[i]);
                            MessageBox.Show("Your friend has rejected your request.");
                            break;
                        }
                    }
                    #endregion
                }
                else
                {
                    #region Acceptance
                    // the remote host explicitly accepted the friend request.
                    System.Net.IPAddress ipComp = System.Net.Dns.GetHostAddresses(finfo.DynamicHost)[0];
                    string tempHostName = ((finfo.IsLAN) ? data.ClientInfo.ClientSocket.RemoteEndPoint.ToString() : ipComp.ToString() + ":" + finfo.ExternalPort);
                    for (int i = 0; i < clientsOnHold.Count; i++)
                    {
                        if (tempHostName.Equals(clientsOnHold[i].ToString()))
                        {
                            // we gots a winner. lawl.
                            int t = 0;
                        saveNewFriend:  // multi try/catch if db is unavailable.
                            try
                            {
                                SubSonic.Generated.FriendX fFriend = new SubSonic.Generated.FriendX();
                                fFriend.MarkNew();
                                fFriend.Port = ((finfo.IsLAN) ? clientsOnHold[i].Port : Convert.ToInt32(finfo.ExternalPort));
                                fFriend.Hostname = ((finfo.IsLAN) ? clientsOnHold[i].Address.ToString() : finfo.DynamicHost);
                                fFriend.Handle = finfo.NickHandle;
                                fFriend.OnLAN = finfo.IsLAN;
                                fFriend.RsaparamXMLString = finfo.PublicKey;
                                fFriend.Save();
                                fFriend = null;
                            }
                            catch
                            {
                                if (t < 4) { goto saveNewFriend; }
                            }
                            clientsOnHold.Remove(clientsOnHold[i]);
                            home.friendConnectWorker.RunWorkerAsync();
                            
                            fForm.RefreshFriendsList();
                            break;
                        }
                    }
                    #endregion
                }
                //data.ClientInfo.ClientSocket.Shutdown(SocketShutdown.Both);  //TODO: Fix this garrett!
            }
             */
            #endregion
            #region Code 602 (incoming search query)
            /*
            if (data.DataType == 602)
            {
                // Deserialize the SearchFor class.
                BinaryFormatter bf = new BinaryFormatter();
                SearchFor sf = (SearchFor)bf.Deserialize(new MemoryStream(data.Data));  // deserialze incoming query

                MemoryStream ms = new MemoryStream();
                DataSet ds = srch.SearchIndex(sf);
                SearchResultInfo sri = new SearchResultInfo();
                sri.Ds = ds;
                sri.Sf = sf;
                bf.Serialize(ms, sri); // perform search and serialize the results
                byte[] resultBytes = ms.GetBuffer();
                server.SendLargeData(resultBytes, 603, data.ClientInfo.ClientSocket);   // send back code 603 w/ the results                
            }
             */
            #endregion
            #region Code 603 (search results)
            /*
            if (data.DataType == 603) 
            {
                // Deserialize the search results (dataset right now);
                BinaryFormatter bf = new BinaryFormatter();
                SearchResultInfo sri = (SearchResultInfo)bf.Deserialize(new MemoryStream(data.Data));  // get the results DS
                // make sure these results match our initial query

                DataSet ds = sri.Ds;
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
                rsa.ImportParameters(data.ClientInfo.PublicKey);
                ds.Tables[0].Columns.Add("owner", Type.GetType("System.String"), "'" + rsa.ToXmlString(false) + "'");
                rsa = null;
                if (sri.Sf.Query.Equals(srch.CurrentSearchInfo.Query))
                {
                    srch.MergeResults(ds);
                }
            }
             */
            #endregion
            #region Code 604 incoming shout txt
            /*
            if (data.DataType == 604)
            {
                string incomingShoutTxt = ASCIIEncoding.UTF8.GetString(data.Data);

                StringBuilder sb = new StringBuilder(ffHelper.GetValueByKey("shout"));
                sb.Insert(0, "\r\n");
                sb.Insert(0, incomingShoutTxt);
                sb.Insert(0, ":: ");
                sb.Insert(0, DateTime.Now.ToShortTimeString());
                sb.Insert(0, " ");
                sb.Insert(0, data.ClientInfo.ClientName);
                
                ffHelper.SetValueByKey("shout", sb.ToString());
                server.SendData(new byte[0], 605, data.ClientInfo.ClientSocket);
                LoadShoutBox();
            }
             */
            #endregion
            #region Code 605 response from the guy we shouted to ealier
            /*
            if (data.DataType == 605)
            {
                SetShoutMsgVisible(true);
            }
            #endregion
            #region Code 900 File Request
            if (data.DataType == 900)
            {
                //Send the requested file
                if (!data.ClientInfo.ClientSocket.Connected)
                {
                    data.ClientInfo.ClientSocket.Connect(data.ClientInfo.ClientSocket.RemoteEndPoint);
                }
                server.SendFile(ASCIIEncoding.ASCII.GetString(data.Data), data.ClientInfo.ClientSocket);
            }
             */
            #endregion
        }
        #endregion

        #region InvokeRequired related methods
        public void SetShoutMsgVisible(bool vis)
        {
            //if (home.InvokeRequired)
            //{
            //    home.Invoke(new SetShoutMsgVisibleDelegate(SetShoutMsgVisible), vis);
            //}
            //else
            //{
            //    if (home.shoutMsg.InvokeRequired)
            //    {
            //        home.shoutMsg.Invoke(new SetShoutMsgVisibleDelegate(SetShoutMsgVisible), vis);
            //    }
            //    else
            //    {
            //        home.shoutMsg.Text = "Shout Received!";
            //        home.shoutSentTimer.Enabled = true;
            //    }
            //}
        }
        private void LoadShoutBox()
        {
            if (home.shoutBox.InvokeRequired)
            {
                home.shoutBox.Invoke(new LoadShoutBoxDelegate(LoadShoutBox));
            }
            else
            {
                home.shoutBox.Text = ffHelper.DBHelper.GetConfig("shout");
            }
        }
        #endregion


        #region vlc netthread code

        private delegate void ChangeActiveTabDelegate(int index);
        private void ChangeActiveTab(int index)
        {
            if (this.tabControl1.InvokeRequired)
            {
                this.tabControl1.Invoke(new ChangeActiveTabDelegate(ChangeActiveTab), index);
            }
            else
            {
                tabControl1.SelectedIndex = index;
            }
        }

        /// <summary>
        /// A class to handle threaded vlc net commands
        /// </summary>
        private class vlc_network_thread
        {

            /// Constructor, saves the file we are looking for and the client to send stuff back to<summary>
            /// A class to handle threaded vlc net commands
            /// </summary>
            /// <param name="file_location">the location (ie: "C:\vid\fish.mpg" of the video to take a snapshot of</param>
            /// <param name="return_user">the user that requested the snapshots</param>
            public vlc_network_thread(string file_location, string return_user, int stream_number, string out_server)
            {
                /*link = file_location;
                //client = return_user;
                //server = out_server;
                //user = client.ToString();
                port = 9050 + stream_number;*/
            }

            /// <summary>
            /// Take 5 screencaps of a video and send them back to the user
            /// </summary>
            public void snapImages()
            {/*
                //create snap directory
                if (!System.IO.Directory.Exists("snapshots\\temp\\" + (port - 9050) + "\\"))
                {
                    System.IO.Directory.CreateDirectory("snapshots\\temp\\" + (port - 9050) + "\\");
                }
                //setup vlc
                System.Windows.Forms.Form hideaway = new System.Windows.Forms.Form();
              //  hideaway.Location = new Point(10000000, 1000000);
                hideaway.Show();
                IntPtr hDT = hideaway.Handle;
                string[] argv = new string[] { "-I", "--ignore-config" };
                libvlc_exception_struct ex = new libvlc_exception_struct();

                //Create new libvlc instance, media instance from file and media player instance from media, set the media to stream to an address
                Marx_libvlc_exception exClass = new Marx_libvlc_exception(ref ex);
                Marx_libvlc_core stream_core = new Marx_libvlc_core(argv, ref ex);
                Marx_libvlc_media stream_media = new Marx_libvlc_media(stream_core.Handle, link, ref ex);
                Marx_libvlc_media_player stream_player = new Marx_libvlc_media_player(stream_media.Handle, ref ex);

                //mute audio, we must be stealthy
                stream_player.audio_set_mute(stream_core.Handle, 1, ref ex);


                //Dispose of media Handle and force garbage collection
                stream_media.Handle.Dispose();
                GC.Collect();
                stream_media = null;

                try
                {
                    stream_player.video_set_parent(stream_core.Handle, hDT, ref ex);
                }
                catch
                {
                    exClass.clear(ref ex);
                    MessageBox.Show("OUTSTREAM HANDLE ATTACH FAILURE");
                }

                stream_player.play(ref ex);

               // hideaway.Hide();
                //take 5 snapshots, save them to the user directory
                for (int i = 1; i < 6; i++)
                {
                    System.Threading.Thread.Sleep(1500); //wait for vlc to catch up
                    stream_player.set_position(i * 0.18f, ref ex);   //set position for snapshot
                    System.Threading.Thread.Sleep(500);      //wait for vlc to catch up again
                    stream_player.video_take_snapshot("snapshots\\temp\\" + (port - 9050) + "\\", 0, 0, ref ex); //take snapshot
                }

                //store image names
                string[] files = System.IO.Directory.GetFiles("snapshots\\temp\\" + (port - 9050) + "\\");

                //send images
                for (int i = 0; i < files.GetLength(0); i++)
                {
                    System.Drawing.Image img = new System.Drawing.Bitmap(files[i]);
                    Image.GetThumbnailImageAbort myCallback = new Image.GetThumbnailImageAbort(ThumbnailCallback);
                    img = img.GetThumbnailImage(144, 81, myCallback, IntPtr.Zero);
                    MemoryStream ms = new MemoryStream();
                    BinaryFormatter bf = new BinaryFormatter();
                    bf.Serialize(ms, img);
                    img.Dispose();
                    img = null;
                    byte[] dest = ms.GetBuffer();
                    server.SendData(dest, 239);
                    System.Threading.Thread.Sleep(1750);
                }

               

                //force garbage collection
                //GC.Collect();
                //GC.WaitForPendingFinalizers();

                //delete images from directory
                if (System.IO.Directory.Exists("snapshots\\temp\\" + (port - 9050) + "\\"))
                {
                    try
                    {
                        System.IO.Directory.Delete("snapshots\\temp\\" + (port-9050) + "\\", true);
                    }
                    catch
                    {
                    }
                }*/                
            }

            /// <summary>
            /// Sends an outgoing udp stream on an unused port
            /// </summary>
            public void stream()
            {
               // System.Windows.Forms.Form hideaway = new System.Windows.Forms.Form();
                //hideaway.Location = new Point(10000000, 1000000);
                //hideaway.Show();
               // IntPtr hDT = hideaway.Handle;
                //string[] argv = new string[] { "-I", "--ignore-config" };
                //libvlc_exception_struct ex = new libvlc_exception_struct();

                //Create new libvlc instance, media instance from file and media player instance from media, set the media to stream to an address
                //Marx_libvlc_exception exClass = new Marx_libvlc_exception(ref ex);
                //Marx_libvlc_core stream_core = new Marx_libvlc_core(argv, ref ex);
                //Marx_libvlc_media stream_media = new Marx_libvlc_media(stream_core.Handle, link, ref ex);
                //string option = ":sout=#duplicate{dst=std{access=udp,dst=" + client.clientSocket.RemoteEndPoint.ToString().Split(':')[0] + ":" + port + "}}";
                //stream_media.add_option(option, ref ex);
                //Marx_libvlc_media_player stream_player = new Marx_libvlc_media_player(stream_media.Handle, ref ex);
                
                //Dispose of media Handle and force garbage collection
                //stream_media.Handle.Dispose();
                //GC.Collect();
                //stream_media = null;

                //try
                //{
                //    stream_player.video_set_parent(stream_core.Handle, hDT, ref ex);
                //}
                //catch
                //{
                //    exClass.clear(ref ex);
                //    MessageBox.Show("OUTSTREAM HANDLE ATTACH FAILURE");
                //}

                //stream_player.play(ref ex);

                //hideaway.Hide();

                ////client.SendData(Encoding.ASCII.GetBytes(port.ToString()), 233);

                //while (!(stream_player.get_position(ref ex) > 0.98f))
                //{
                //    System.Threading.Thread.Sleep(500);
                //    if (pause)
                //    {
                //        pause = false;
                //        stream_player.pause(ref ex);
                //        if (paused == false)
                //        {
                //            paused = true;
                //        }
                //        else
                //        {
                //            paused = false;
                //        }
                //    }
                //    if (restart)
                //    {
                //        if (paused)
                //        {
                //            stream_player.pause(ref ex);
                //            paused = false;
                //        }
                //        else
                //        {
                //            stream_player.play(ref ex);
                //        }
                //    }
                //    if (seek > -1.0f)
                //    {
                //        seek = -1.0f;
                //        stream_player.set_position(seek, ref ex);
                //    }
                //}

                //GC.Collect();
                //GC.WaitForPendingFinalizers();
            }
        }
        #endregion

        #region server_ClientConnected
        /// <summary>
        /// Event raised when a new client connects to this server
        /// </summary>
        /// <param name="client">Client that connected</param>
        void server_clientConnectedEvent(string client)
        {/*
            RSACryptoServiceProvider tempProvider = new RSACryptoServiceProvider();
            tempProvider.ImportParameters(client.PublicKey);
            string key = tempProvider.ToXmlString(false);
            SubSonic.Generated.FriendXCollection myFriend = null;

            int connectBack = 0;
            foreach (Client c in Clients)
            {
                if (c.BsPublicKey.Equals(key))
                {
                    connectBack++;
                }
            }

            if (connectBack.Equals(0))
            {
                int i = 0;
            loadMyFriendCollection:
                try
                {
                    i++;
                    myFriend = new SubSonic.Generated.FriendXController().FetchAll();
                }
                catch
                {
                    if (i < 10) { goto loadMyFriendCollection; }
                }
                RSACryptoServiceProvider priv = new RSACryptoServiceProvider();
                priv.FromXmlString(ffHelper.GetValueByKey("privatekey"));
                RSACryptoServiceProvider pub = new RSACryptoServiceProvider();
                pub.FromXmlString(ffHelper.GetValueByKey("personalkey"));

                foreach (SubSonic.Generated.FriendX f in myFriend)// there should only be one that matches
                {
                    if (f.RsaparamXMLString.Equals(key))    // duh!
                    {
                        // a friend is connecting to us connect back now.
                        Client tClient = new NetworkLibrary.Client(f.Hostname,
                                        (int)f.Port,
                                        pub.ExportParameters(false),
                                        priv.ExportParameters(true),
                                        ffHelper.GetValueByKey("displayname"));
                        tClient.OnDataReceived += new NetworkLibrary.Common.DataReceived(server_dataReceivedEvent);
                        tClient.OnConnected += new NetworkLibrary.Common.OnClientConnected(client_OnConnected);
                        tClient.OnFileTransferStart += new Common.OnFileTransferStart(tClient_OnFileTransferStart);
                        tClient.FileProgressUpdate += new Common.ProgressUpdate(tClient_FileProgressUpdate);
                        Clients.Add(tClient);
                        break; // only 1 friend can be this guy
                    }
                }
            }*/
        }
        #endregion
        #region client_OnConnected
        public void client_OnConnected(string client)
        {/*
            // update the clients list
            RSACryptoServiceProvider tempProvider = new RSACryptoServiceProvider();
            tempProvider.ImportParameters(client.PublicKey);
            string key = tempProvider.ToXmlString(false);
            SubSonic.Generated.FriendXCollection friends = null;

            int i = 0;
        getJustConnectedClient:
            try
            {
                i++;
                friends = new SubSonic.Generated.FriendXController().FetchAll();
            }
            catch
            {
                if (i < 10) { goto getJustConnectedClient; }
            }
            RSACryptoServiceProvider priv = new RSACryptoServiceProvider();
            priv.FromXmlString(ffHelper.GetValueByKey("privatekey"));
            RSACryptoServiceProvider pub = new RSACryptoServiceProvider();
            pub.FromXmlString(ffHelper.GetValueByKey("personalkey"));
            foreach (SubSonic.Generated.FriendX f in friends)
            {
                if (f.RsaparamXMLString.Equals(key))
                {
                    this.AddnUpdateFriendsList(new FriendsInAList(f.Handle + "->" + f.Hostname + ":" + f.Port.ToString(), f.RsaparamXMLString));
                    break; // only 1 friend can be this guy
                }
            }*/
        }
        #endregion

        private void AddnUpdateFriendsList(FriendsInAList fList)
        {
            if (home.InvokeRequired)
            {
                home.Invoke(new AddnUpdateFriendsListDelegate(AddnUpdateFriendsList), fList);
            }
            else
            {
                if (home.lstFriendChat.Items.Contains("No friends online."))
                {
                    home.lstFriendChat.Items.Remove("No friends online.");
                }
                home.lstFriendChat.Items.Add(fList);
                if (home.lstFriendChat.Items.Count > 0)
                {
                    home.lstFriendChat.SelectedIndex = 0;
                }
            }
        }

        #region FormLoad
        /// <summary>
        /// Event raised when the program is first started, initializes the form and links user controls to it
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
            formLoadOperations.RunWorkerAsync();

            #region (1)Load the Video Control
            //this must be loaded first
            TabPage tabVideo = new TabPage("Player");
            tabVideo.Name = "Player";
            tabVideo.BackColor = Color.Transparent;
            video = new UserControls.Video(ref ffHelper, this);
            video.Dock = DockStyle.Fill;
            tabVideo.Controls.Add(video);
            tabControl1.TabPages.Add(tabVideo);
            #endregion
            #region (2)Load the Library Control
            TabPage tabLibrary = new TabPage("Library");
            tabLibrary.Name = "Library";
            tabLibrary.BackColor = Color.Transparent;
            id = new UserControls.Indexer(ref ffHelper);
            id.Dock = DockStyle.Fill;
            tabLibrary.Controls.Add(id);
            tabControl1.TabPages.Add(tabLibrary);
            #endregion
            #region (3)Load the Search Control
            TabPage tabSearch = new TabPage("Search");
            tabSearch.Name = "Search";
            tabSearch.BackColor = Color.Transparent;
            srch = new UserControls.Search(ref video, ref tabControl1, ref ffHelper);
            srch.Dock = DockStyle.Fill;
            tabSearch.Controls.Add(srch);
            tabControl1.TabPages.Add(tabSearch);
            #endregion
            #region (4)Load the Transfer Control
            TabPage tabTransfer = new TabPage("Transfers");
            tabTransfer.Name = "Transfers";
            tabTransfer.BackColor = Color.Transparent;
            trans = new UserControls.Transfers(ref ffHelper, this);
            trans.Dock = DockStyle.Fill;
            tabTransfer.Controls.Add(trans);
            tabControl1.TabPages.Add(tabTransfer);
            #endregion
            #region (5)Load the Home Control
            TabPage tabHome = new TabPage("Home");
            tabHome.Name = "Home";
            tabHome.BackColor = Color.Transparent;
            home = new UserControls.Home(this);
            home.Dock = DockStyle.Fill;
            tabHome.Controls.Add(home);
            tabControl1.TabPages.Add(tabHome);
            #endregion

            #region order the tabs set their properties.
            TabPage temp = tabControl1.TabPages[0];

            tabControl1.TabPages[0] = tabControl1.TabPages[4];
            tabControl1.TabPages[4] = temp;
            temp = tabControl1.TabPages[1];
            tabControl1.TabPages[1] = tabControl1.TabPages[2];
            tabControl1.TabPages[2] = tabControl1.TabPages[3];
            tabControl1.TabPages[3] = temp;

            int tabWidth = (this.ClientRectangle.Width / tabControl1.TabPages.Count) - 1;
            tabControl1.SizeMode = TabSizeMode.Fixed;
            tabControl1.ItemSize = new Size(tabWidth, 30);
            foreach (TabPage t in tabControl1.TabPages)
            {
                t.Refresh();
            }
            #endregion

            #region Load the Player Control
            plControls = new UserControls.PlayerControls(ref srch, ref video, ref ffHelper, this);
            plControls.Dock = DockStyle.Fill;
            globalBottomPanel.Controls.Add(plControls);
            #endregion
            // network stuff
        }
        #endregion

        #region AddFriend
        /// <summary>
        /// Performs a series of options and queries to
        /// negotiate a friend request.
        /// </summary>
        /// <param name="ip"></param>
        /// <returns></returns>
        public bool SendAddFriendRequest(string hostNamen)
        {

            // send command to request public key.
            // send them our public key.
            RSACryptoServiceProvider priv = new RSACryptoServiceProvider();
            priv.FromXmlString(ffHelper.DBHelper.GetConfig("privatekey"));
            RSACryptoServiceProvider pub = new RSACryptoServiceProvider();
            pub.FromXmlString(ffHelper.DBHelper.GetConfig("personalkey"));


            // ask the client if they want to be friends
            string myPublicKeyOffer = ffHelper.DBHelper.GetConfig("personalkey");
            if (!myPublicKeyOffer.Equals(""))
            {
                //clientsOnHold.Add(ip);
                AddFriendInfo info = new AddFriendInfo();
                info.PublicKey = myPublicKeyOffer;
                info.NickHandle = ffHelper.DBHelper.GetConfig("displayname");    //tell the other guy my nickname/handle

               // object[] pass = new object[] { info, tClient };
                //addFriendWorker.RunWorkerAsync(pass);
            }
            else
            {
                // failed to send my key as an offer.
            }
            return true;
        }

        private void addFriendWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            System.Threading.Thread.Sleep(1000);
            object[] temp = (object[])e.Argument;
            //Client c = (Client)temp[1];
            AddFriendInfo infoz = (AddFriendInfo)temp[0];
            MemoryStream ms = new MemoryStream();
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(ms, infoz);
            byte[] bytesToSend = ms.GetBuffer();

            // take the client we just made, and perform the handshake
            //c.SendData(bytesToSend, 600);
        }

        private void addFriendWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // nothing in this event for now.
            // not really needed since the netcode is async as well.
            // but may have a use in the future.
        }
        #endregion

        #region formLoadOperations
        private void formLoadOperations_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            //Nothing for now. Goes with DoWork obviously
        }

        private void formLoadOperations_DoWork(object sender, DoWorkEventArgs e)
        {
            // Nothing for now
        }
        #endregion

        #region AboutBox
        /// <summary>
        /// Launches the AboutBox
        /// </summary>
        /// <param name="sender">Object sending the event</param>
        /// <param name="e">any event arguments that need to be passed</param>
        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AboutBox ab = new AboutBox();
            ab.ShowDialog();
        }
        #endregion
        #region FAQ
        /// <summary>
        /// FAQ Link
        /// </summary>
        /// <param name="sender">Object sending the event</param>
        /// <param name="e">any event arguments that need to be passed</param>
        private void fAQToolStripMenuItem_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("http://code.google.com/p/friendlyfire/wiki/FrequentlyAskedQuestions");
        }
        #endregion


        #region TabControl IndexChanged
        /// <summary>
        /// Performs certain actions when
        /// the selected tabindex is changed
        /// </summary>
        /// <param name="sender">Object sending the event</param>
        /// <param name="e">any event arguments that need to be passed</param>
        private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Makes sure the search box is always the first
            // item to have focus when it is selected.
            // - The user is abel to immediately start typing
            // - their query.
            if (tabControl1.SelectedTab.Text == "Search")
                srch.ActivateSearchBoxField();
            //else if (tabControl1.SelectedTab.Text == "Player")
            //{
            //    // play a stream if available
            //    if (video.StreamPath.Text != "")
            //    {
            //        video.PlayFile(video.StreamPath.Text);
            //    }
            //}
        }
        #endregion

        #region custom owner drawn tabcontrol tabs
        // the index of the current hot-tracking tab
        private int hotTrackTab = -1;

        /// <summary>
        /// Function that finds which tab is being moused over
        /// </summary>
        /// <returns>Integer value of the cursor the mouse is over</returns>
        private int GetTabUnderCursor()
        {
            Point cursor = this.tabControl1.PointToClient(Cursor.Position);
            for (int i = 0; i < this.tabControl1.TabPages.Count; i++)
            {
                if (this.tabControl1.GetTabRect(i).Contains(cursor))
                    return i;
            }
            return -1;
        }

        /// <summary>
        /// This function finds the current mouse position and which tab it is over
        /// </summary>
        private void UpdateHotTrack()
        {
            int hot = GetTabUnderCursor();
            if (hot != this.hotTrackTab)
            {
                // invalidate the old hot-track item to remove hot-track effects
                if (this.hotTrackTab != -1)
                    this.tabControl1.Invalidate(this.tabControl1.GetTabRect(this.hotTrackTab));

                this.hotTrackTab = hot;

                // invalidate the new hot-track item to add hot-track effects
                if (this.hotTrackTab != -1)
                    this.tabControl1.Invalidate(this.tabControl1.GetTabRect(this.hotTrackTab));

                // force the tab to redraw invalidated regions
                this.tabControl1.Update();
            }
        }

        /// <summary>
        /// This custom draws the 2-tone tabs and enables hot-tracking to show the currently selected and moused over tab
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
        {
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

            Font tabFont = new Font("Verdana", 11f, FontStyle.Bold);
            StringFormat tabStringFormat = new StringFormat();
            tabStringFormat.Alignment = StringAlignment.Center;
            tabStringFormat.LineAlignment = StringAlignment.Center;

            int maxColor = 150;

            // draw the background based on hot tracking
            if (e.Index == this.hotTrackTab)
            {
                using (SolidBrush themeB = new SolidBrush(ffHelper.CurrentGradientColor))
                {
                    Brush b = Brushes.Black;
                    if (themeB.Color == Color.Blue || themeB.Color.B < maxColor && themeB.Color.R < maxColor && themeB.Color.G < maxColor)
                    {
                        b = Brushes.White;
                    }

                    SolidBrush alpha = new SolidBrush(Color.FromArgb(150, ffHelper.CurrentGradientColor));
                    e.Graphics.FillRectangle(alpha, e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height / 2);
                    e.Graphics.FillRectangle(themeB, e.Bounds.X, e.Bounds.Y + e.Bounds.Height / 2, e.Bounds.Width, e.Bounds.Height / 2);
                    e.Graphics.DrawString(tabControl1.TabPages[e.Index].Text,
                                            tabFont, b,
                                            new RectangleF(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height),
                                            tabStringFormat);
                    alpha.Dispose();
                }
            }
            else
            {
                using (SolidBrush themeB = new SolidBrush(ffHelper.CurrentGradientColor))
                {
                    Brush b = Brushes.Black;

                    if (themeB.Color == Color.Blue || themeB.Color.B < maxColor && themeB.Color.R < maxColor && themeB.Color.G < maxColor)
                    {
                        b = Brushes.White;
                    }
                    SolidBrush alpha = new SolidBrush(Color.FromArgb(150, ffHelper.CurrentGradientColor));
                    //e.DrawBackground();
                    e.Graphics.FillRectangle(themeB, e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height / 2);
                    e.Graphics.FillRectangle(alpha, e.Bounds.X, e.Bounds.Y + e.Bounds.Height / 2, e.Bounds.Width, e.Bounds.Height / 2);
                    e.Graphics.DrawString(tabControl1.TabPages[e.Index].Text,
                                                tabFont, b,
                                                new RectangleF(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height),
                                                tabStringFormat);
                    alpha.Dispose();
                }
            }

            // draw the text label for the item, other effects, etc
        }

        private void tabControl1_MouseEnter(object sender, EventArgs e)
        {
            UpdateHotTrack();
        }

        private void tabControl1_MouseLeave(object sender, EventArgs e)
        {
            UpdateHotTrack();
        }

        private void tabControl1_MouseMove(object sender, MouseEventArgs e)
        {
            UpdateHotTrack();
        }
        #endregion

        #region ToolStripMenu Stuff
        /// <summary>
        /// This event shows the config form when configuration is selected in the top menu
        /// </summary>
        /// <param name="sender">Object sending the event</param>
        /// <param name="e">any event arguments that need to be passed</param>
        private void generalConfigurationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ConfigOptions co = new ConfigOptions(this);
            DialogResult dr = co.ShowDialog();
            if (dr == DialogResult.OK)
            {
                this.Refresh();
            }
            co.Dispose();
        }
        /// <summary>
        /// This event displays the friend form when selected from the top menu
        /// </summary>
        /// <param name="sender">Object sending the event</param>
        /// <param name="e">any event arguments that need to be passed</param>
        private void addRemoveFriendsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Friends fs = new Friends(ref ffHelper);
            this.fForm = fs;
            DialogResult dr = fs.ShowDialog();
            if (dr == DialogResult.OK)
            {
                this.Refresh();
            }
            fs.Dispose();
        }
        /// <summary>
        /// Event raised when the media option is clicked in the top menu, this function retrieves all audio, video, and subtitle options from a video file and updates the menu respectively
        /// </summary>
        /// <param name="sender">Object sending the event</param>
        /// <param name="e">any event arguments that need to be passed</param>
        private void mediaToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //TODO: retrieve video information here
        }

        /// <summary>
        /// Event that plays or resumes video when the play option is picked from the top menu
        /// </summary>
        /// <param name="sender">Object sending the event</param>
        /// <param name="e">any event arguments that need to be passed</param>
        private void playToolStripMenuItem_Click(object sender, EventArgs e)
        {
            video.vactx.playlist.play();
            video.vactx.Visible = true;
        }

        /// <summary>
        /// Event that pauses video playback when the pause option is picked from the top menu
        /// </summary>
        /// <param name="sender">Object sending the event</param>
        /// <param name="e">any event arguments that need to be passed</param>
        private void pauseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try { video.vactx.playlist.togglePause(); }
            catch (Exception err)
            {
            }
        }
        /// <summary>
        /// Event that stops video playback when the stop option is picked from the top menu
        /// </summary>
        private void stopToolStripMenuItem_Click(object sender, EventArgs e)
        {
            video.vactx.playlist.stop();
            video.vactx.Visible = false;
        }
        #endregion

        #region Drawing Overrides
        /// <summary>
        /// This overrides the paint event allowing for gradients and double buffering to reduce resize flicker
        /// </summary>
        /// <param name="sender">Object sending the event</param>
        /// <param name="e">any event arguments that need to be passed</param>
        private void FriendlyFire_Paint(object sender, PaintEventArgs e)
        {
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
        }

        /// <summary>
        /// Thiss function makes sure all gradiants and forms resize properly and do not crash when the program is resized
        /// </summary>
        /// <param name="sender">Object sending the event</param>
        /// <param name="e">any event arguments that need to be passed</param>
        private void FriendlyFire_Resize(object sender, EventArgs e)
        {
            int tabWidth = (this.ClientRectangle.Width / tabControl1.TabPages.Count) - 1;
            if (tabWidth > 0)
            {
                tabControl1.ItemSize = new Size(tabWidth, 30);
            }
        }
        #endregion

        #region Accessors
        public UserControls.Home Home
        {
            get { return home; }
            set { home = value; }
        }
        public UserControls.Transfers Trans
        {
            get { return trans; }
            set { trans = value; }
        }
        public UserControls.Video Video
        {
            get { return video; }
            set { video = value; }
        }
        public UserControls.Search Srch
        {
            get { return srch; }
            set { srch = value; }
        }
        #endregion

        #region FriendlyFire_FormClosing
        /// <summary>
        /// Attempt a graceful shutdown.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FriendlyFire_FormClosing(object sender, FormClosingEventArgs e)
        {
            //TODO send shutdown messge to the thread running the FFListenerController.
        }
        #endregion


        #region Secondary Tab Switching Menu Actions
        private void homeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.tabControl1.SelectedIndex = this.tabControl1.TabPages.IndexOf(this.tabControl1.TabPages["Home"]);
        }

        private void searchToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.tabControl1.SelectedIndex = this.tabControl1.TabPages.IndexOf(this.tabControl1.TabPages["Search"]);
        }

        private void libraryToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.tabControl1.SelectedIndex = this.tabControl1.TabPages.IndexOf(this.tabControl1.TabPages["Library"]);
        }

        private void indexerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.tabControl1.SelectedIndex = this.tabControl1.TabPages.IndexOf(this.tabControl1.TabPages["Indexer"]);
        }

        private void transfersToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.tabControl1.SelectedIndex = this.tabControl1.TabPages.IndexOf(this.tabControl1.TabPages["Transfers"]);
        }

        private void playerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.tabControl1.SelectedIndex = this.tabControl1.TabPages.IndexOf(this.tabControl1.TabPages["Player"]);
        }
        #endregion

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }
    }
}
